#include <pthread.h>
#include <signal.h>

#include "storage_image.h"
#include "get_image.h"
#include "get_list.h"
#include "list_image.h"
#include "../libreria.h"
#include "../response_storage_image.h"
#include "../response_get_image.h"
#include "../response_get_list.h"

#define QUEUE_SIZE 5
#define NUM_SERVICE 3

				
char nome_servizio[NUM_SERVICE][BUFFER_SIZE];	// contiene i nomi dei servizi passati da riga di comando 
List_Image * lista;				// lista di immagini

char ip_service[INET_ADDRSTRLEN] = "127.0.0.1";	// indirizzo IP di ascolto del Service Provider richieste del client
int porta_service; 				// porta di ascolto del Service Provider richieste del client

char ip_service_register[INET_ADDRSTRLEN];	// indirizzo IP del Service Register
int porta_service_register;			// porta del Service Register



void deregistra(int sig_receive)
{
// Funzione che deregistra service_provider dal Registro dei Servizi
// Va in esecuzione dopo la pressione del CTRL+C

	char msg[BUFFER_SIZE];
	memset(msg,0,BUFFER_SIZE);
	int num_service;
	int i;
	int s_deregistrazione;
	
	///////  CREAZIONE DEL SOCKET PER COMUNICARE COL SERVICE PROVIDER   //////
	s_deregistrazione=inizializza_client(ip_service_register, porta_service_register);	
	
	for (i=0; strcmp(nome_servizio[i],"#")!=0 && i<NUM_SERVICE; i++);
	num_service=i;

	// Invio messaggio del tipo "1;nome_servizio;porta_service;ip_service"	
	sprintf(msg,"1;%d;%d;%s#",num_service,porta_service,ip_service);


	if(send(s_deregistrazione,msg,strlen(msg),0) == -1)
			printf("Errore comunicazione con Service Register\n");


	// Invio di un messaggio per ogni Servizio da deregistrare
	for (int i=0; i<num_service; i++)
	{
		
		memset(msg,0,BUFFER_SIZE);
		sprintf(msg,"%s;",nome_servizio[i]);
		if(send(s_deregistrazione,msg,strlen(msg),0) == -1)
			printf("Errore comunicazione con Service Register\n");

	}
	
	printf("Ho deregistrato tutti i servizi\n");
	
	close(s_deregistrazione);

	exit(1);
}




///////////////////// INIZIO THREAD ////////////////////////////////////////

void* gestisci_client(void *From)	
{
	
	host mitt;
	int i;
	int choice;
	char buf[BUFFER_SIZE];
	bool verify;

	mitt = *(host *)(From);//in mitt vanno i dati relativi all'IP, porta e socket mittente
	char buffer [BUFFER_SIZE],file[BUFFER_SIZE];
	
	////// ricevo stringa codificata in buffer fino a carattere '#' /////

	memset(buffer,0,BUFFER_SIZE);
	memset(file,0,BUFFER_SIZE);
	i=0;

	do
		recv (mitt.s,&buffer[i],1,0);
	
	while (buffer[i++]!='#');

	///////////////////////////////////////////////
	
	char nome_servizio[BUFFER_SIZE];
	memset(nome_servizio,0,BUFFER_SIZE);
	i=0;
	while(buffer[i]!=';' && buffer[i]!='#')
	{
		nome_servizio[i]=buffer[i];
		i++;
	}	
	
	printf("Richiesta di servizio %s dal client (%s:%d).\n",nome_servizio,mitt.ip,mitt.porta);

	if(!strcmp("Storage_Image",nome_servizio))
		choice=0;
	else if(!strcmp("Get_Image",nome_servizio))
			choice=1;
		else if(!strcmp("Get_List",nome_servizio))
			choice=2;
		     else
			 choice=-1;
	
	
	
	// In nome_servizio c'è il servizio effettivo che ha richiesto il client

	switch (choice)
	{
	case 0: 
		
	//Servizio di Store_Image

		{	
		
		char nome_file[BUFFER_SIZE],lunghezza_file[BUFFER_SIZE];
		
		Response_Storage_Image rsi(mitt);

		Storage_Image si (mitt);
		
		verify=rsi.decode_request(&buffer[strlen(nome_servizio)+1],&nome_file[0],lunghezza_file); // riceve richiesta da parte del client
													  // e la decodifica 						
		
		if(!verify)
		{		
			rsi.code_response(verify);	// se verify==false --> invia al client "0;"
			printf("Ricevuta richiesta da client (%s:%d) di Storage_Image non corretta!\n",mitt.ip,mitt.porta);
			break;// interrompe il thread

		}
			
		lista->richiestaScrittura( nome_file ); 
		
		rsi.code_response(verify); 	// codifica risposta al client "1;" in modo che il client invii l'immagine
	
		sprintf(file,"foto/%s",nome_file); //scrivo nel path foto/ la foto che ricevo dal client

		verify=si.Store_image(file,atoi(lunghezza_file)); //riceve immagine dal client
		
		lista->rilascioScrittura( nome_file );		

		if(!verify)
			printf("Errore nella creazione del file durante Store_image()\n");
		else
			printf("Ricevuta da client (%s:%d) immagine %s.\n",mitt.ip,mitt.porta,nome_file);

		}
		break;

	case 1:
		{
		
		//Servizio di Get_Image
		
		char nome_file[BUFFER_SIZE];
		int len_file;

		Response_Get_Image rgi(mitt);

		Get_Image gi(mitt);

		verify=rgi.decode_request(&buffer[strlen(nome_servizio)+1],&nome_file[0]); // riceve richiesta da parte del client
											   // e la decodifica

		
		if(!verify)
			{
			 printf("Ricevuta richiesta da client (%s:%d) di Storage_Image non corretta!\n",mitt.ip,mitt.porta);
			 rgi.code_response(0,verify); // se verify==false --> invia al client "0;"
			 break; // interrompe il thread
			}			
		
		lista->richiestaLettura( nome_file );
	
		
		sprintf(buf,"foto/%s",nome_file);
	
		len_file=get_len_image(buf); // ritorna lunghezza dell'immagine da inviare
	
		if(!rgi.code_response(len_file,verify)) // codifica risposta al client "1;" 
		{		
			printf("Errore di comunicazione con client\n");
			break;
		}
		
		gi.Get_image(nome_file); //invio effettivo dell'immagine

		lista->rilascioLettura( nome_file );
	
		printf("Inviata a client (%s:%d) immagine %s.\n",mitt.ip,mitt.porta,nome_file);
		
		}
		break;
	
	case 2:{ 

		//Servizio di Get_List

		char buf[BUFFER_SIZE];
	

		Response_Get_List rgl(mitt);
		
		Get_List gl(mitt);		
		
		
		gl.Get_list(lista,buf); // invio della lista dei file disponibili
	
		if(!rgl.code_response(buf))
			printf("Errore nella comunicazione con il client (%s:%d) durante Get_List.\n",mitt.ip,mitt.porta);
		else
			printf("Inviata a client (%s:%d) la lista immagini delle immagini disponibili.\n",mitt.ip,mitt.porta);
		
		}
		break;
		
	}
	
	close (mitt.s);
	return NULL;
}

int main (int argc, char * argv[])
{
	struct sockaddr_in client_addr;

	pthread_t tid; 		//identificatore thread
	int i;
	int s;		
	char msg [BUFFER_SIZE];
	int s_listening;    	//socket di listening
	FILE *f;
	char nome_foto[BUFFER_SIZE];
	
	int s_accept;		//socket di connessione
	socklen_t len = sizeof(struct sock_addr*);//lunghezza in byte della struttura sock_addr

	signal(SIGINT,deregistra);



	//Generazione porta random compresa tra 49152 e 65535 in cui il Service Provider si mette in ascolto

	srand(time(NULL));
	porta_service= rand()%16383;
	porta_service+=49152;
	
	///////////////////////////////////////////////////////

		
	////// CONTROLLO PARAMETRI DA RIGA DI COMANDO   /////

	if (argc > (3+NUM_SERVICE) || argc < 4)
	{
			printf("Errore numero parametri:\nUsage: ./ServiceProvider1 ip_service_register porta_service_register nome_servizio1 [nome_servizio2] [nome_servizio3]\n");;
	}
	
	printf("SERVICE PROVIDER, v.1.0\n");

	
	porta_service_register = atoi(argv[2]);	// porta del Service Register
	strcpy(ip_service_register,argv[1]);	// IP del Service Register

		
	///// INIZIALIZZAZIONE SOCKET PER CONNESSIONE AL SERVICE REGISTER //////
	
	s=inizializza_client(ip_service_register,porta_service_register);
	
	//////////////////////////////////////////////////////////////////
	
	for(i=0; i<NUM_SERVICE;i++)
	
		strcpy(nome_servizio[i],"#");
	

	////////////  REGISTRAZIONE DI SERVIZI SU SERVICE REGISTER  ////////////


	// Invio messaggio del tipo "0;num_servizi;nome_servizio;porta;ip#"
	
	sprintf(msg,"0;%d;%d;%s#",(argc-3),porta_service,ip_service);
	
	if(send(s,msg,strlen(msg),0) == -1)
			printf("Errore comunicazione con Service Register\n");

	memset(msg,0,BUFFER_SIZE);

	// Invio uno dietro l'altro dei nomi dei servizi da registrare
	// separati da ';'

	for (int i=0; i<(argc-3); i++)
	{
		strcat(msg,argv[i+3]);
		strcat(msg,";");
		strcpy(nome_servizio[i],argv[i+3]);

	}
	if(send(s,msg,strlen(msg),0) == -1)
			printf("Errore comunicazione con Service Register\n");
	
	/////////////////////////////////////////////////////////////////////

	


	///// INIZIALIZZAZIONE SOCKET DI ASCOLTO PER I CLIENT ///////	
	
	s_listening=inizializza_server(ip_service,porta_service,QUEUE_SIZE);
	
	printf ("Attesa di connessioni da client...\n");

	if(system ("find foto/*.jpg > ./foto.txt")==-1)
		{		
		printf("Errore nell'esecuzione del comando find nella cartella foto\n");
		exit(1);
		}

	
	//// INIZIALIZZAZIONE DELLA LISTA DELLE IMMAGINI   ////////////

	lista= new List_Image(); // Inizializzo lista di immagini contenute in "foto/foto.txt"

	f=fopen("foto.txt","r");
	
	while(fscanf(f,"%s",nome_foto)!= EOF)
		{
		i=0;
		while(nome_foto[i++]!='/');
		lista->add_image(&nome_foto[i]); //in questo modo a add_image passiamo il nome immagine senza path
		memset(nome_foto,0,BUFFER_SIZE);
		}

	fclose (f);

	////////////////////////////////////////////////////////////////


	while(1)
	{

		//////////  ATTESA richieste da Client /////////////

		s_accept = accept(s_listening,(struct sockaddr*) &client_addr, &len);//creo il socket di connnessione
		if (s_accept == (-1))
		{ 
			printf ("ERRORE nella connessione!\n");
			exit(1);
		}
		
		host client;
		inet_ntop(AF_INET,&client_addr.sin_addr,client.ip,sizeof(client.ip));
		client.s = s_accept;
		client.porta = ntohs(client_addr.sin_port);

		//////// CREAZIONE di un thread per la gestione del Client /////////////

		if (pthread_create (&tid, NULL, gestisci_client,(void *)&client)!=0) 
		{
			printf("Errore nella creazione del Thread...\n");
			exit(1);
		}	

		memset(&client_addr,0,sizeof(client_addr));
	}
		
	

}





